﻿#region License
/* ---------------------------------------------------------------- *
 *
 * This file is part of the Xcoordination Application Space
 * ("XcoAppSpace") http://www.xcoordination.com
 *
 * Copyright (C) 2009 Xcoordination http://www.xcoordination.com
 *
 * XcoAppSpace is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General
 * Public License as published by the Free Software Foundation.
 *
 * XcoAppSpace is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/
 * or write to the Free Software Foundation, Inc., 51 Franklin
 * Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * ---------------------------------------------------------------- */
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using XcoAppSpaces.Contracts.CcrWiring;
using XcoAppSpaces.Contracts.Communication;
using Microsoft.Ccr.Core;
using XcoAppSpaces.Contracts.Messaging;
using XcoAppSpaces.Contracts.Worker;
using XcoAppSpaces.Contracts.Service;
using TypeResolve;
using XcoAppSpaces.Contracts.Exceptions;

namespace XcoAppSpaces.Core
{
	/// <summary>
	/// Wrapper class for access to the local workers by the Core.Communication components.
	/// </summary>
	internal class LocalWorkerRegistry : IXcoWorkerRegistry, IDisposable
	{
		private readonly Dictionary<string, XcoWorkerInfo> localWorkers = new Dictionary<string, XcoWorkerInfo>();

		public IEnumerable<XcoWorkerInfo> Workers
		{
			get { return localWorkers.Values; }
		}

		public LocalWorkerRegistry()
		{
			DefineSimplifiedIdentifierTypeNames();
		}

        public void AddWorker<TMessageContract>(WorkerInfo<TMessageContract> worker, string name, PublishMode publish)
			where TMessageContract : IPort
		{
			var workerId = GenerateIdentifier(typeof(TMessageContract), name);
			var newWorker = new XcoWorkerInfo(worker.Instance, typeof(TMessageContract), name, publish, worker.ServiceAttributes, worker.ProcessorAttributes, false, null, null, workerId);
			lock (localWorkers)
			{
				CheckWorkerExists(workerId);
				localWorkers.Add(workerId, newWorker);
			}
			if (WorkerAdded != null)
				WorkerAdded(this, newWorker);
		}

        public void AddWorkerProxy<TMessageContract>(TMessageContract instance, string name, PublishMode publish, string address, WorkerProxyInfo proxyInfo)
			where TMessageContract : IPort
		{
			var workerId = GenerateIdentifier(typeof(TMessageContract), name);
			var newWorker = new XcoWorkerInfo(instance, typeof(TMessageContract), name, publish, null, proxyInfo.ProcessorAttributes, true, address, proxyInfo.CommServiceName, workerId);
			lock (localWorkers)
			{
				CheckWorkerExists(workerId);
				localWorkers.Add(workerId, newWorker);
			}
			if (WorkerAdded != null)
				WorkerAdded(this, newWorker);
		}

		private void CheckWorkerExists(string workerId)
		{
			if (localWorkers.ContainsKey(workerId))
				throw new XcoWorkerException("A worker with the contract/name " + workerId + " already exists in the local appspace. Please use a unique combination of contract and local name to run/connect to a worker.");
		}

		public void Remove(IPort worker)
		{
			XcoWorkerInfo workerInfo = null;
			lock (localWorkers)
			{
				workerInfo = localWorkers.Values.FirstOrDefault(w => w.Worker == worker);
				if (workerInfo != null)
					localWorkers.Remove(workerInfo.Identifier);
			}
			if (workerInfo != null && WorkerRemoved != null)
				WorkerRemoved(this, workerInfo);
		}

		#region IXcoWorkerRegistry Members

		public event WorkerEventHandler WorkerAdded;

		public event WorkerEventHandler WorkerRemoved;

		public void Register<TContract>(TContract instance, string name) where TContract : IPort
		{
			lock (localWorkers)
			{
				var workerId = GenerateIdentifier(typeof(TContract), name);
				localWorkers.Add(workerId, new XcoWorkerInfo(instance, typeof(TContract), name, PublishMode.Published, null, null, false, null, null, workerId));
			}
		}

		public bool Exists(string workerIdentifier, bool onlyPublished, bool connectionRequest)
		{
			lock (localWorkers)
			{
				XcoWorkerInfo worker;
				if (localWorkers.TryGetValue(workerIdentifier, out worker))
				{
					if (onlyPublished && !worker.IsPublished)
						return false;
					if (connectionRequest)
						worker.NumberOfConnections++;
					return true;
				}
				return false;
			}
		}

		public XcoWorkerInfo Resolve(string workerIdentifier, bool onlyPublished)
		{
			lock (localWorkers)
			{
				XcoWorkerInfo worker;
				if (localWorkers.TryGetValue(workerIdentifier, out worker))
				{
					if (onlyPublished && !worker.IsPublished)
						return null;
					return worker;
				}
				return null;
			}
		}

		public TMessageContract Resolve<TMessageContract>(string name)
		{
			var workerId = GenerateIdentifier(typeof(TMessageContract), name);
			var workerInfo = Resolve(workerId, false);
			return (workerInfo != null ? (TMessageContract)workerInfo.Worker : default(TMessageContract));
		}

		public string GenerateIdentifier(Type workerType, string workerName)
		{
			var typeStr = TypeResolver.TypeToString(workerType);
			return (workerName != null) ?
				typeStr + "/" + workerName :
				typeStr;
		}

		private static void DefineSimplifiedIdentifierTypeNames()
		{
			//TypeResolver is used for generating worker identifiers, as well as for serializing types to strings for JSON serialization
			//the names defined here help producing shorter and simpler identifiers
			TypeResolver.AddTypeMapping(typeof(Port<>), "port`1");
			TypeResolver.AddTypeMapping(typeof(PortSet<,>), "portset`2");
			TypeResolver.AddTypeMapping(typeof(PortSet<,,>), "portset`3");
			TypeResolver.AddTypeMapping(typeof(PortSet<,,,>), "portset`4");
			TypeResolver.AddTypeMapping(typeof(PortSet<,,,,>), "portset`5");
			TypeResolver.AddTypeMapping(typeof(WorkerMessage), "workermessage");
			TypeResolver.AddTypeMapping(typeof(RemotePortMessage), "remoteportmessage");
			TypeResolver.AddTypeMapping(typeof(XcoPortSerializationInfo), "xcoportserializationinfo");
		}

		#endregion

		#region IDisposable Members

		public void Dispose()
		{
			foreach (XcoWorkerInfo workerInfo in localWorkers.Values)
			{
				if (workerInfo.Worker is IDisposable)
					((IDisposable)workerInfo.Worker).Dispose();
			}
		}

		#endregion

		#region IXcoService Members

		public void Initialize(XcoAppSpaces.Contracts.Service.IXcoServiceRegistry serviceRegistry)
		{
			//nothing to do
		}

		#endregion

		
	}
}
